package com.haocjia.esdemo.elastic.service.impl;

import com.haocjia.esdemo.elastic.service.ElasticSearchService;
import lombok.RequiredArgsConstructor;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
public class ElasticSearchServiceImpl implements ElasticSearchService {

    private final RestHighLevelClient client;

    @Override
    public List<Map<String, Object>> search(String[] fields, String val, Integer pageNum,
                                            Integer pageSize, String sort, String... index) throws IOException {
        SearchRequest searchRequest = new SearchRequest(index);
        searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());
        searchRequest.preference("_local");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.multiMatchQuery(val, fields));
        sourceBuilder.from((pageNum - 1) * pageSize);
        sourceBuilder.size(pageSize);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        sourceBuilder.sort(new FieldSortBuilder(sort).order(SortOrder.DESC));
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = searchResponse.getHits();
        SearchHit[] searchHits = hits.getHits();
        List<Map<String, Object>> list = new ArrayList<>();
        for (SearchHit hit : searchHits) {
            list.add(hit.getSourceAsMap());
        }
        return list;
    }



    @Override
    public GetResponse get(String index, Long id) throws IOException {
        Boolean exists = exists(index, id);
        if (exists) {
            GetRequest request = new GetRequest(index);
            request.id(id.toString());
            return client.get(request, RequestOptions.DEFAULT);
        }
        return null;
    }

    @Override
    public IndexResponse add(String index, String jsonString, Long id) throws IOException {
        Boolean exists = exists(index, id);
        if (!exists) {
            IndexRequest request = new IndexRequest(index);
            request.id(id.toString());
            request.source(jsonString, XContentType.JSON);
            return client.index(request, RequestOptions.DEFAULT);
        }
        return null;
    }

    @Override
    public UpdateResponse update(String index, String jsonString, Long id) throws IOException {
        UpdateRequest request = new UpdateRequest(index, id.toString());
        request.upsert(jsonString, XContentType.JSON);
        return client.update(request, RequestOptions.DEFAULT);
    }

    @Override
    public Boolean exists(String index, Long id) throws IOException {
        GetRequest getRequest = new GetRequest(index, id.toString());
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        getRequest.storedFields("_none_");
        return client.exists(getRequest, RequestOptions.DEFAULT);
    }

    @Override
    public DeleteResponse deleteById(String index, Long id) throws IOException {
        DeleteRequest request = new DeleteRequest(index, id.toString());
        return client.delete(request, RequestOptions.DEFAULT);
    }

    @Override
    public CreateIndexResponse createIndex(String index, String mapping, Integer shards, Integer replicas) throws IOException {
        CreateIndexRequest request = new CreateIndexRequest(index);
        if (mapping != null) {
            request.mapping(mapping, XContentType.JSON);
        }
        request.settings(Settings.builder()
                .put("index.number_of_shards", shards == null ? 0 : shards)
                .put("index.number_of_replicas", replicas == null ? 0 : replicas)
                .build());
        return client.indices().create(request, RequestOptions.DEFAULT);
    }

    @Override
    public Boolean exists(String index) throws IOException {
        GetIndexRequest request = new GetIndexRequest(index);
        return client.indices().exists(request, RequestOptions.DEFAULT);
    }

    @Override
    public AcknowledgedResponse deleteIndex(String index) throws IOException {
        DeleteIndexRequest request = new DeleteIndexRequest(index);
        return client.indices().delete(request, RequestOptions.DEFAULT);
    }
}
